﻿using System;
using Emgu.CV;
using OpenCVDemo.IService;
using Emgu.CV.CvEnum;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Drawing;
using Emgu.CV.Structure;
using Emgu.CV.Util;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace OpenCVDemo.Service
{
    public class EmguCVService : IOpenCVService
    {
        /// <summary>
        /// Hello EmguCV
        /// </summary>
        public void Hello()
        {
            Mat srcImg = CvInvoke.Imread("images/car.jpg");
            CvInvoke.Imshow("img", srcImg);
            CvInvoke.WaitKey();
        }

        /// <summary>
        /// 图像操作入门
        /// </summary>
        public void BeginImage()
        {
            //输入(读取)
            Mat srcMat = CvInvoke.Imread("images/car.jpg", ImreadModes.Color);

            //图像显示
            if (srcMat.IsEmpty)
            {
                Console.WriteLine("未找到图像");
                return;
            }
            CvInvoke.Imshow("srcMat", srcMat);
            Console.WriteLine("原图像的宽度:{0}px", srcMat.Cols);
            Console.WriteLine("原图像的高度:{0}px", srcMat.Rows);
            Console.WriteLine("原图像的通道数:{0}", srcMat.NumberOfChannels);
            CvInvoke.WaitKey(0);

            //图像处理
            Mat destMat = new Mat();
            CvInvoke.CvtColor(srcMat, destMat, ColorConversion.Bgr2Gray);//彩色转换为灰度

            //输出(保存)
            CvInvoke.Imwrite("images/car2.jpg", destMat);

            //销毁资源
            CvInvoke.DestroyAllWindows();
        }

        /// <summary>
        /// 视频摄像图操作
        /// </summary>
        public void VideoCapture()
        {
            VideoCapture videoCapture = new VideoCapture("videos/Video_2021-11-12_150456.wmv");
            if (!videoCapture.IsOpened)
            {
                Console.WriteLine("Open video failed!");
                return;
            }

            Mat frame = new Mat();
            while (true)
            {
                videoCapture.Read(frame);
                if (frame.IsEmpty)
                {
                    Console.WriteLine("Video is empty!");
                    break;
                }

                CvInvoke.Imshow("video", frame);
                if (CvInvoke.WaitKey(30) > 0)//按任意键退出
                {
                    break;
                }
            }
        }

        /// <summary>
        /// 实践应用1
        /// </summary>
        public void PracticeCase1()
        {
            //1，视频分解图片            
            //VideoCapture videoCapture = new VideoCapture("./videos/2.avi");
            //if (!videoCapture.IsOpened)
            //{
            //    Console.WriteLine("Open video failed!");
            //    return;
            //}

            //Mat frame = new Mat();
            //string filename = "";
            //int i = 0;
            //while (true)
            //{
            //    videoCapture.Read(frame);
            //    if (frame.IsEmpty)
            //    {
            //        Console.WriteLine("Video is empty!");
            //        break;
            //    }

            //    i++;
            //    filename = string.Format("./output/{0}.jpg", i);
            //    CvInvoke.Imshow("video", frame);
            //    CvInvoke.Imwrite(filename, frame);
            //    if (CvInvoke.WaitKey(30) > 0)//按任意键退出
            //    {
            //        break;
            //    }
            //}

            //2，批量读取图片
            //for (int i = 1; i <= 644; i++)
            //{
            //    string fileName = "./output/" + i.ToString() + ".jpg";
            //    Mat img = CvInvoke.Imread(fileName);
            //    CvInvoke.Imshow("img", img);
            //    if (CvInvoke.WaitKey(30) > 0)//按任意键退出
            //    {
            //        break;
            //    }
            //}

            //3，图片合成视频
            //VideoWriter videoWriter = new VideoWriter("1.avi", -1, 20, new Size(568, 320), true);
            //Mat frame = new Mat();
            //for (int i = 1; i <= 644; i++)
            //{
            //    string fileName = "./output/" + i.ToString() + ".jpg";
            //    frame = CvInvoke.Imread(fileName);
            //    videoWriter.Write(frame);
            //    if (CvInvoke.WaitKey(30) > 0)//按任意键退出
            //    {
            //        break;
            //    }
            //}
        }

        /// <summary>
        /// 认识Mat与颜色空间
        /// </summary>
        public void MatAndColor()
        {
            //初始化Mat
            Mat img1 = new Mat("./images/input-20211108.png");//等价于：Mat img1 = CvInvoke.Imread("./images/car.jpg");

            //浅拷贝与深拷贝
            Mat img2 = img1;//浅拷贝
            Mat img3 = img1.Clone();//深拷贝

            //颜色转换
            Mat img4 = new Mat();
            CvInvoke.CvtColor(img1, img4, ColorConversion.Bgr2Hsv);
            CvInvoke.Imshow("img4", img4);
            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 绘图函数，主要用于识别结果的标记
        /// </summary>
        public void DrawFunction()
        {
            //1，绘制直线
            Mat img = new Mat(500, 500, DepthType.Cv8U, 3);
            img.SetTo(new MCvScalar(0, 0, 0));

            //CvInvoke.Line(img, new Point(10,10), new Point(50, 50), new MCvScalar(0, 255, 0),2);

            //2，绘制圆
            //CvInvoke.Circle(img, new Point(200, 200), 100, new MCvScalar(0, 0, 255), 2);

            //3，绘制椭圆
            //CvInvoke.Ellipse(img, new RotatedRect(new PointF(250, 250), new SizeF(200, 300), 0),new MCvScalar(0, 255, 255), 2);

            //4，绘制矩形
            //CvInvoke.Rectangle(img, new Rectangle(100, 100, 300, 200), new MCvScalar(255, 0, 0), 2);

            //5，添加文字
            CvInvoke.PutText(img, "Hello EmguCV", new Point(10, 200), FontFace.HersheyComplex, 2, new MCvScalar(0, 255, 255));

            //显示图像
            CvInvoke.Imshow("img", img);
            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 访问图像像素
        /// </summary>
        public void ImagePixel()
        {
            Image<Bgr, byte> img = new Image<Bgr, byte>("images/1.jpg");

            //1，直接访问像素
            //for (int i = 0; i < img.Rows; i++)
            //{
            //    for (int j = 0; j < img.Cols; j++)
            //    {
            //        if (i == j)
            //        {
            //            img[i, j] = new Bgr(0, 0, 255);
            //        }
            //    }
            //}

            //2，通过Image的Data属性访问像素
            //for (int i = 0; i < img.Rows; i++)
            //{
            //    for (int j = 0; j < img.Cols; j++)
            //    {
            //        if (i == j)
            //        {
            //            img.Data[i, j, 0] = 0;
            //            img.Data[i, j, 1] = 255;
            //            img.Data[i, j, 2] = 0;
            //        }
            //    }
            //}

            //3，实例：图像减色
            //byte reduceValue = 64; //减色系数(值越大输出颜色越少)
            //for (int i = 0; i < img.Rows; i++)
            //{
            //    for (int j = 0; j < img.Cols; j++)
            //    {
            //        img.Data[i, j, 0] = (byte)((byte)((byte)(img.Data[i, j, 0] / reduceValue) * reduceValue) + (byte)(reduceValue / 2)); //蓝色通道或灰度图像
            //        img.Data[i, j, 1] = (byte)((byte)((byte)(img.Data[i, j, 1] / reduceValue) * reduceValue) + (byte)(reduceValue / 2)); //绿色通道
            //        img.Data[i, j, 2] = (byte)((byte)((byte)(img.Data[i, j, 2] / reduceValue) * reduceValue) + (byte)(reduceValue / 2)); //红色通道
            //    }
            //}

            //4，实例：雪花效果
            int snowNum = 1000;//雪花或者椒盐噪声的数量
            Random random = new Random();
            for (int k = 0; k < snowNum; k++)
            {
                int i = random.Next() % img.Rows;
                int j = random.Next() % img.Cols;
                img.Data[i, j, 0] = 255;
                img.Data[i, j, 1] = 255;
                img.Data[i, j, 2] = 255;
            }

            //显示图像
            CvInvoke.Imshow("img", img);
            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 图像运算
        /// </summary>
        public void ImageOperation()
        {
            Mat img1 = CvInvoke.Imread("images/4.jpg");
            Mat img2 = CvInvoke.Imread("images/5.jpg");
            Mat dstImg = new Mat();

            //1，算术运算
            //加法
            //CvInvoke.Add(img1, img2, dstImg);
            //CvInvoke.AddWeighted(img1, 0.5, img2, 0.5, 0, dstImg);

            //减法
            //CvInvoke.Subtract(img1, img2, dstImg);
            //CvInvoke.AbsDiff(img1, img2, dstImg);

            Mat temp = new Mat(new Size(img1.Cols, img1.Rows), DepthType.Cv8U, 3);
            temp.SetTo(new MCvScalar(1, 1, 1));
            //乘法
            //CvInvoke.Multiply(img1, temp, dstImg, 1);

            //除法
            //CvInvoke.Divide(img1, temp, dstImg, 1);

            //2，逻辑运算
            //与
            //CvInvoke.BitwiseAnd(img1, img2, dstImg);

            //或
            //CvInvoke.BitwiseOr(img1, img2, dstImg);

            //取反
            //CvInvoke.BitwiseNot(img1, dstImg);

            //异或
            CvInvoke.BitwiseXor(img1, img2, dstImg);

            //显示图像
            CvInvoke.Imshow("img", dstImg);
            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 对比度与亮度调整，通道分离
        /// </summary>
        public void ContrastBrightness()
        {
            //对比度与亮度调整
            //Image<Bgr, Byte> img = new Image<Bgr, Byte>("images/6.jpg");
            //Image<Bgr, Byte> img2 = img.Clone();
            //int contrast = 150;//对比度
            //int brightness = 0;//亮度
            //for (int i = 0; i < img.Rows; i++)
            //{
            //    for (int j = 0; j < img.Cols; j++)
            //    {
            //        int B = (int)((contrast * 0.01) * img.Data[i, j, 0] + brightness);
            //        int G = (int)((contrast * 0.01) * img.Data[i, j, 1] + brightness);
            //        int R = (int)((contrast * 0.01) * img.Data[i, j, 2] + brightness);
            //        if (B > 255)
            //            B = 255;
            //        if (G > 255)
            //            G = 255;
            //        if (R > 255)
            //            R = 255;
            //        if (B < 0)
            //            B = 0;
            //        if (G < 0)
            //            G = 0;
            //        if (R < 0)
            //            R = 0;
            //        img.Data[i, j, 0] = (byte)B;
            //        img.Data[i, j, 1] = (byte)G;
            //        img.Data[i, j, 2] = (byte)R;
            //    }
            //}

            //通道分离与合并
            //分离
            Mat img = CvInvoke.Imread("images/6.jpg");
            Mat[] Channels = img.Split();
            CvInvoke.Imshow("B", Channels[0]);
            CvInvoke.Imshow("G", Channels[1]);
            CvInvoke.Imshow("R", Channels[2]);

            //合并
            Mat dst = new Mat();
            VectorOfMat vChannels = new VectorOfMat();
            CvInvoke.Threshold(Channels[0], Channels[0], 100, 255, ThresholdType.Binary);
            CvInvoke.Threshold(Channels[1], Channels[1], 100, 255, ThresholdType.Binary);
            CvInvoke.Threshold(Channels[2], Channels[2], 100, 255, ThresholdType.Binary);

            vChannels.Push(Channels[0]);
            vChannels.Push(Channels[1]);
            vChannels.Push(Channels[2]);
            CvInvoke.Merge(vChannels, dst);
            CvInvoke.Imshow("merge", dst);


            //显示图像
            CvInvoke.Imshow("result", img);
            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// ROI与Mask掩码
        /// </summary>
        public void ROIMask()
        {
            //ROI区域的设置与保存
            //Mat img = CvInvoke.Imread("images/7.jpg");
            //CvInvoke.Imshow("src", img);
            //Mat roi = new Mat(img, new Rectangle(213, 236, 494, 246)).Clone();
            ////Mat ROI = new Mat(img, new Range(236, 236 + 246), new Range(213, 213 + 494)).Clone();

            //CvInvoke.Imwrite("output/roi.jpg", roi);
            //CvInvoke.Imshow("roi", roi);

            //直接融合
            //Mat img = CvInvoke.Imread("images/7.jpg");
            //Mat logo = CvInvoke.Imread("images/8.jpg");
            //CvInvoke.Imshow("src", img);
            //CvInvoke.Imshow("logo", logo);

            //Mat roi = new Mat(img, new Rectangle(10, 10, logo.Cols, logo.Rows));
            //logo.CopyTo(roi);

            //CvInvoke.Imshow("roi", roi);
            //CvInvoke.Imshow("result", img);

            //使用Mask融合
            Mat img = CvInvoke.Imread("images/7.jpg");
            Mat logo = CvInvoke.Imread("images/8.jpg");
            CvInvoke.Imshow("src", img);
            CvInvoke.Imshow("logo", logo);

            Mat mask = new Mat();
            CvInvoke.CvtColor(logo, mask, ColorConversion.Bgr2Gray);//彩色转灰度图
            CvInvoke.Imshow("gray", mask); ;

            CvInvoke.BitwiseNot(mask, mask);//逻辑与，目的是让背景变成全黑
            CvInvoke.Imshow("bitwise_not", mask);

            CvInvoke.Threshold(mask, mask, 100, 255, ThresholdType.Binary);//二值化，目的是让灰度值大于100的设为最大255，其它的值为0
            CvInvoke.Imshow("threshold", mask);

            Mat roi = new Mat(img, new Rectangle(10, 10, logo.Cols, logo.Rows));
            logo.CopyTo(roi, mask);

            CvInvoke.Imshow("roi", roi);
            CvInvoke.Imshow("result", img);

            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 图像几何变换
        /// </summary>
        public void ImageGeometricTransform()
        {
            //图像缩放
            //Mat srcImg = CvInvoke.Imread("images/7.jpg");
            //CvInvoke.Imshow("src", srcImg);

            //Mat destImg = new Mat();
            ////CvInvoke.Resize(srcImg, destImg, new Size(300,200));
            //CvInvoke.Resize(srcImg, destImg, new Size(), 0.5, 0.5);
            //CvInvoke.Imshow("dest", destImg);

            //图像平移
            //1)不改变图像大小，信息会丢失
            //Image<Bgr, Byte> srcImg = new Image<Bgr, byte>("images/7.jpg");
            //CvInvoke.Imshow("src", srcImg);

            //int xOffset = 100, yOffset = 100;
            //int dstRows = srcImg.Rows;
            //int dstCols = srcImg.Cols;
            //Image<Bgr, Byte> dstImg = new Image<Bgr, Byte>(dstCols, dstRows);
            //ImgTranslate(srcImg, dstImg, xOffset, yOffset);
            //CvInvoke.Imshow("dst", dstImg);

            //2)会改变图像大小，信息不丢失
            //Image<Bgr, Byte> srcImg = new Image<Bgr, byte>("images/7.jpg");
            //CvInvoke.Imshow("src", srcImg);

            //int xOffset = 100, yOffset = 100;
            //int dstRows = srcImg.Rows + Math.Abs(yOffset);
            //int dstCols = srcImg.Cols + Math.Abs(xOffset);
            //Image<Bgr, Byte> dstImg = new Image<Bgr, Byte>(dstCols, dstRows);
            //ImgTranslate(srcImg, dstImg, xOffset, yOffset);
            //CvInvoke.Imshow("dst", dstImg);

            //图像旋转
            //Mat srcImg = CvInvoke.Imread("images/7.jpg");
            //CvInvoke.Imshow("src", srcImg);

            //PointF center = new PointF(srcImg.Cols / 2, srcImg.Rows / 2);
            //double angle = 45;
            //double scale = 1;

            //Mat rotateMat = new Mat();
            //CvInvoke.GetRotationMatrix2D(center, angle, scale, rotateMat);

            //Mat dstImg = new Mat();
            //CvInvoke.WarpAffine(srcImg, dstImg, rotateMat, new Size(srcImg.Cols, srcImg.Rows));
            //CvInvoke.Imshow("dst", dstImg);

            //转置镜像
            //Mat srcImg = CvInvoke.Imread("images/7.jpg");
            //CvInvoke.Imshow("src", srcImg);

            //Mat dstImg = new Mat();
            ////CvInvoke.Transpose(srcImg, dstImg);
            ////CvInvoke.Flip(srcImg, dstImg, FlipType.Horizontal);
            ////CvInvoke.Flip(srcImg, dstImg, FlipType.Vertical);
            //CvInvoke.Flip(srcImg, dstImg, FlipType.Both);

            //CvInvoke.Imshow("dst", dstImg);

            //重映射
            Image<Bgr, Byte> srcImg = new Image<Bgr, byte>("images/7.jpg");
            CvInvoke.Imshow("src", srcImg);

            Image<Gray, Single> xMatImg = new Image<Gray, Single>(srcImg.Cols, srcImg.Rows);
            Image<Gray, Single> yMatImg = new Image<Gray, Single>(srcImg.Cols, srcImg.Rows);

            for (int i = 0; i < srcImg.Rows; i++)
            {
                for (int j = 0; j < srcImg.Cols; j++)
                {
                    xMatImg[i, j] = new Gray(j);
                    yMatImg[i, j] = new Gray(i + 5 * Math.Sin(j / 10.0));
                }
            }

            Mat dstImg = new Mat();
            CvInvoke.Remap(srcImg, dstImg, xMatImg, yMatImg, Inter.Area);
            CvInvoke.Imshow("dst", dstImg);

            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 图像滤波，用于图像预处理，去除噪声和使图像变得平滑
        /// </summary>
        public void ImageFilter()
        {
            //线性滤波：方框滤波，均值滤波，高斯滤波
            //非线性滤波：中值滤波，双边滤波

            Mat srcImg = CvInvoke.Imread("images/7.jpg");
            CvInvoke.Imshow("src", srcImg);

            Mat dstImg = new Mat();
            //CvInvoke.BoxFilter(srcImg, dstImg, DepthType.Cv8U, new Size(3, 3), new Point(-1, -1), true);//方框滤波
            //CvInvoke.Blur(srcImg, dstImg, new Size(3, 3), new Point(-1, -1));//均值滤波
            CvInvoke.GaussianBlur(srcImg, dstImg, new Size(3, 3), 0, 0);//高斯滤波
            //CvInvoke.MedianBlur(srcImg, dstImg, 7);//中值滤波
            //CvInvoke.BilateralFilter(srcImg, dstImg, 9, 20.0, 5.0);//双边滤波
            CvInvoke.Imshow("dst", dstImg);

            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 图像阈值化，用于图像分割
        /// </summary>
        public void ImageThreshold()
        {
            Mat srcImg = CvInvoke.Imread("images/9.png");
            CvInvoke.Imshow("src", srcImg);

            Mat grayImg = new Mat();
            CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            CvInvoke.Imshow("gray", grayImg);

            CvInvoke.Threshold(grayImg, grayImg, 220, 255, ThresholdType.BinaryInv);
            //CvInvoke.AdaptiveThreshold(grayImg, grayImg, 220, AdaptiveThresholdType.GaussianC, ThresholdType.BinaryInv, 11, 5);
            CvInvoke.Imshow("threshold", grayImg);

            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 图像膨胀与腐蚀
        /// </summary>
        public void DilateErode()
        {
            //膨胀
            Mat srcImg = CvInvoke.Imread("images/9.png");
            CvInvoke.Imshow("src", srcImg);

            Mat grayImg = new Mat();
            CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            CvInvoke.Imshow("gray", grayImg);

            CvInvoke.Threshold(grayImg, grayImg, 220, 255, ThresholdType.BinaryInv);
            //CvInvoke.AdaptiveThreshold(grayImg, grayImg, 220, AdaptiveThresholdType.GaussianC, ThresholdType.BinaryInv, 11, 5);
            CvInvoke.Imshow("threshold", grayImg);

            Mat dstImg = new Mat();
            Mat element = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(5, 1), new Point(-1, -1));
            //CvInvoke.Dilate(grayImg, dstImg, element, new Point(-1, -1), 1, BorderType.Default, new MCvScalar());
            //CvInvoke.Imshow("dilate", dstImg);

            //腐蚀
            //CvInvoke.Erode(grayImg, dstImg, element, new Point(-1, -1), 1, BorderType.Default, new MCvScalar());
            //CvInvoke.Imshow("erode", dstImg);

            //形态学其它操作，包括：开运算，闭运算，顶帽，黑帽，形态学梯度
            CvInvoke.MorphologyEx(grayImg, dstImg, MorphOp.Dilate, element, new Point(-1, -1), 1, BorderType.Default, new MCvScalar());//等价于CvInvoke.Dilate
            CvInvoke.Imshow("morphologyEx", dstImg);

            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 边缘检测
        /// </summary>
        public void EdgeDetection()
        {
            //Canny边缘检测
            Mat srcImg = CvInvoke.Imread("images/9.png");
            CvInvoke.Imshow("src", srcImg);

            Mat grayImg = new Mat();
            CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            CvInvoke.Imshow("gray", grayImg);

            //阈值化
            CvInvoke.Threshold(grayImg, grayImg, 220, 255, ThresholdType.BinaryInv);
            //CvInvoke.AdaptiveThreshold(grayImg, grayImg, 220, AdaptiveThresholdType.GaussianC, ThresholdType.BinaryInv, 11, 5);
            CvInvoke.Imshow("threshold", grayImg);

            CvInvoke.Canny(grayImg, grayImg, 100, 220);
            CvInvoke.Imshow("canny", grayImg);

            //TODO:常用的还有：Sobel，Laplacian边缘检测

            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 霍夫变换，一种特征提取技术，主要用来检测直线或圆
        /// </summary>
        public void HoughTransform()
        {
            //标准霍夫变换
            //Mat srcImg = CvInvoke.Imread("images/10.jpg");
            //CvInvoke.Imshow("src", srcImg);

            //Mat grayImg = new Mat();
            //CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            //CvInvoke.Canny(grayImg, grayImg, 30, 100);

            //VectorOfPointF lines = new VectorOfPointF();
            //CvInvoke.HoughLines(grayImg, lines, 10, Math.PI / 90, 200);
            //for (int i = 0; i < lines.Size; i++)
            //{
            //    float rho = lines[i].X, theta = lines[i].Y;
            //    Point pt1 = new Point();
            //    Point pt2 = new Point();
            //    double a = Math.Cos(theta), b = Math.Sin(theta);
            //    double x0 = a * rho, y0 = b * rho;
            //    pt1.X = (int)Math.Round(x0 + 600 * (-b));
            //    pt1.Y = (int)Math.Round(y0 + 600 * a);
            //    pt2.X = (int)Math.Round(x0 - 600 * (-b));
            //    pt2.Y = (int)Math.Round(y0 - 600 * a);
            //    CvInvoke.Line(srcImg, pt1, pt2, new MCvScalar(0, 255, 0), 1);
            //}
            //CvInvoke.Imshow("dst", srcImg);

            //累积概率霍夫变换
            //Mat srcImg = CvInvoke.Imread("images/10.jpg");
            //CvInvoke.Imshow("src", srcImg);

            //Mat grayImg = new Mat();
            //CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            //CvInvoke.Canny(grayImg, grayImg, 30, 200);

            //LineSegment2D[] lines = CvInvoke.HoughLinesP(grayImg, 1, Math.PI / 180, 30, 5, 10);
            //for (int i = 0; i < lines.Length; i++)
            //{
            //    Point pt1 = lines[i].P1;
            //    Point pt2 = lines[i].P2;
            //    CvInvoke.Line(srcImg, pt1, pt2, new MCvScalar(0, 255, 0), 2);
            //}
            //CvInvoke.Imshow("dst", srcImg);

            //霍夫圆变换
            Mat srcImg = CvInvoke.Imread("images/6.jpg");
            CvInvoke.Imshow("src", srcImg);

            Mat grayImg = new Mat();
            CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);

            CircleF[] circles = CvInvoke.HoughCircles(grayImg, HoughModes.Gradient, 1, 80, 100, 30, 120, 200);
            for (int i = 0; i < circles.Length; i++)
            {
                Console.WriteLine("{0}", i);
                CvInvoke.Circle(srcImg, new Point((int)circles[i].Center.X, (int)circles[i].Center.Y), (int)circles[i].Radius, new MCvScalar(255, 255, 0), 5);
            }
            CvInvoke.Imshow("dst", srcImg);

            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 直方图及应用
        /// </summary>
        public void Hist()
        {
            //1，直方图计算
            //Mat srcImg = CvInvoke.Imread("images/11.jpg");
            //CvInvoke.Imshow("src", srcImg);

            //Mat grayImg = new Mat();
            //CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            //CvInvoke.Imshow("gray", grayImg);

            //Mat dHist = new Mat();
            //int[] channels = new int[] { 0 };
            //float[] ranges = new float[] { 0, 255 };
            //int[] histSize = new int[] { 256 };
            //VectorOfMat vMatImgs = new VectorOfMat();
            //vMatImgs.Push(grayImg);
            //CvInvoke.CalcHist(vMatImgs, channels, new Mat(), dHist, histSize, ranges, false);

            //2，直方图均衡化，主要用来增强图像对比度
            //Mat srcImg = CvInvoke.Imread("images/11.jpg", ImreadModes.Grayscale);
            //CvInvoke.Imshow("src", srcImg);

            //Mat dstImg = new Mat();
            //CvInvoke.EqualizeHist(srcImg, dstImg);
            //CvInvoke.Imshow("dst", dstImg);

            //彩色图像分离三通道，分别对每个通道进行直方图均衡化，再进行通道合并
            //Mat srcImg = CvInvoke.Imread("images/11.jpg");
            //CvInvoke.Imshow("src", srcImg);

            //Mat[] Channels = srcImg.Split();
            //CvInvoke.Imshow("B", Channels[0]);
            //CvInvoke.Imshow("G", Channels[1]);
            //CvInvoke.Imshow("R", Channels[2]);

            //CvInvoke.EqualizeHist(Channels[0], Channels[0]);
            //CvInvoke.EqualizeHist(Channels[1], Channels[1]);
            //CvInvoke.EqualizeHist(Channels[2], Channels[2]);

            ////合并
            //Mat dst = new Mat();
            //VectorOfMat vChannels = new VectorOfMat();
            //vChannels.Push(Channels[0]);
            //vChannels.Push(Channels[1]);
            //vChannels.Push(Channels[2]);
            //CvInvoke.Merge(vChannels, dst);
            //CvInvoke.Imshow("dst", dst);

            //3，直方图对比
            //Mat srcImg = CvInvoke.Imread("images/11.jpg");
            //Mat srcImg2 = CvInvoke.Imread("images/11.jpg");
            //CvInvoke.Imshow("src", srcImg);

            //Mat grayImg = new Mat();
            //Mat grayImg2 = new Mat();
            //CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            //CvInvoke.CvtColor(srcImg2, grayImg2, ColorConversion.Bgr2Gray);

            //Mat dHist = new Mat();
            //Mat dHist2 = new Mat();
            //int[] channels = new int[] { 0 };
            //float[] ranges = new float[] { 0, 255 };
            //int[] histSize = new int[] { 256 };
            //VectorOfMat vMatImgs = new VectorOfMat();
            //vMatImgs.Push(grayImg);
            //VectorOfMat vMatImgs2 = new VectorOfMat();
            //vMatImgs2.Push(grayImg2);
            //CvInvoke.CalcHist(vMatImgs, channels, new Mat(), dHist, histSize, ranges, false);
            //CvInvoke.CalcHist(vMatImgs2, channels, new Mat(), dHist2, histSize, ranges, false);

            //double matchValue0 = CvInvoke.CompareHist(dHist, dHist2, HistogramCompMethod.Correl);
            //double matchValue1 = CvInvoke.CompareHist(dHist, dHist2, HistogramCompMethod.Chisqr);
            //double matchValue2 = CvInvoke.CompareHist(dHist, dHist2, HistogramCompMethod.Intersect);
            //double matchValue3 = CvInvoke.CompareHist(dHist, dHist2, HistogramCompMethod.Bhattacharyya);
            //Console.WriteLine("match0 = {0}\nmatch1 = {1}\nmatch2 = {2}\nmatch3 = {3}\n", matchValue0, matchValue1, matchValue2, matchValue3);

            //4，反向投影，主要用来在输入图像(大)中查找与特定图像(模板图像), 最匹配的点或者区域, 也就是定位模板图像出现在输入图像的位置
            Mat srcImg = CvInvoke.Imread("images/14.jpg");
            CvInvoke.Imshow("src", srcImg);

            Mat hsvImg = new Mat();
            CvInvoke.CvtColor(srcImg, hsvImg, ColorConversion.Bgr2Hsv);

            Mat dHist = new Mat();
            int[] channels = new int[] { 0 };
            float[] ranges = new float[] { 0, 180 };
            int[] histSize = new int[] { 20 };//组距(直方图柱子数量)
            VectorOfMat vMatImgs = new VectorOfMat();
            vMatImgs.Push(hsvImg);
            CvInvoke.CalcHist(vMatImgs, channels, new Mat(), dHist, histSize, ranges, false);
            CvInvoke.Normalize(dHist, dHist, 0, 255, NormType.MinMax);

            Mat backProject = new Mat();
            CvInvoke.CalcBackProject(vMatImgs, channels, dHist, backProject, ranges);
            CvInvoke.Imshow("backproject", backProject);

            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 模板匹配
        /// </summary>
        public void MatchTemplate()
        {
            //单模板匹配
            //Mat srcImg = CvInvoke.Imread("images/18.png");
            //CvInvoke.Imshow("src", srcImg);
            //Mat result = srcImg.Clone();

            //Mat tempImg = CvInvoke.Imread("images/19.png");
            //int dstImg_rows = srcImg.Rows - tempImg.Rows + 1;
            //int dstImg_cols = srcImg.Cols - tempImg.Cols + 1;
            //Mat dstImg = new Mat(dstImg_rows, dstImg_cols, DepthType.Cv32F, 1);
            //CvInvoke.MatchTemplate(srcImg, tempImg, dstImg, TemplateMatchingType.CcoeffNormed);
            //CvInvoke.Imshow("match", dstImg);

            ////矩阵归一化
            //CvInvoke.Normalize(dstImg, dstImg, 0, 1, NormType.MinMax, dstImg.Depth);

            //double minValue = 1000, maxValue = -1;
            //Point minLoc = new Point(0, 0);
            //Point maxLoc = new Point(0, 0);
            //CvInvoke.MinMaxLoc(dstImg, ref minValue, ref maxValue, ref minLoc, ref maxLoc);//寻找最值
            //CvInvoke.Rectangle(result, new Rectangle(maxLoc.X, maxLoc.Y, tempImg.Width, tempImg.Height),new MCvScalar(0, 255, 0), 2);//标出识别结果
            //CvInvoke.Imshow("result", result);

            //视频模板匹配


            //多模板匹配
            Mat srcImg = CvInvoke.Imread("images/16.png");
            CvInvoke.Imshow("src", srcImg);
            Mat result = srcImg.Clone();

            Mat tempImg = CvInvoke.Imread("images/17.png");
            int dstImg_rows = srcImg.Rows - tempImg.Rows + 1;
            int dstImg_cols = srcImg.Cols - tempImg.Cols + 1;
            Mat dstImg = new Mat(dstImg_rows, dstImg_cols, DepthType.Cv32F, 1);
            CvInvoke.MatchTemplate(srcImg, tempImg, dstImg, TemplateMatchingType.CcoeffNormed);
            CvInvoke.Imshow("match", dstImg);

            CvInvoke.Normalize(dstImg, dstImg, 0, 1, NormType.MinMax, dstImg.Depth);
            Image<Gray, Single> ImgMatch = dstImg.ToImage<Gray, Single>();
            int count = 0;
            int tempW = 0, tempH = 0;
            for (int i = 0; i < dstImg_rows; i++)
            {
                for (int j = 0; j < dstImg_cols; j++)
                {
                    float matchValue = ImgMatch.Data[i, j, 0];
                    if (matchValue >= 0.65 && (Math.Abs(j - tempW) > 10) && (Math.Abs(i - tempH) > 10))
                    {
                        count++;
                        CvInvoke.Rectangle(result, new Rectangle(j, i, tempImg.Width, tempImg.Height), new MCvScalar(0, 255, 0), 2);
                        tempW = j;
                        tempH = i;
                    }
                }
            }
            Console.WriteLine("{0}", count);
            CvInvoke.Imshow("result", result);

            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 查找和绘制轮廓
        /// </summary>
        public void FindDrawContours()
        {
            Mat srcImg = CvInvoke.Imread("images/9.png");
            CvInvoke.Imshow("src", srcImg);

            Mat grayImg = new Mat();
            CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            CvInvoke.Imshow("gray", grayImg);

            //二值化
            CvInvoke.Threshold(grayImg, grayImg, 220, 255, ThresholdType.BinaryInv);
            CvInvoke.Imshow("threshold", grayImg);

            //膨胀
            Mat element = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 1), new Point(-1, -1));
            CvInvoke.Dilate(grayImg, grayImg, element, new Point(-1, -1), 1, BorderType.Default, new MCvScalar());
            CvInvoke.Imshow("dilate", grayImg);

            //查找轮廓
            VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            CvInvoke.FindContours(grayImg, contours, null, RetrType.External, ChainApproxMethod.ChainApproxSimple);

            //绘制轮廓
            List<VectorOfPoint> vectorOfPoints = new List<VectorOfPoint>();
            for (int i = 0; i < contours.Size; i++)//遍历每个轮廓
            {
                vectorOfPoints.Add(contours[i]);
            }
            Rectangle[] rectangles = vectorOfPoints.Select(p => CvInvoke.BoundingRectangle(p)).Where(q => q.Width > 400 && q.Height > 100).ToArray();
            Console.WriteLine("rectangle_num={0}", rectangles.Length);

            for (int i = 0; i < rectangles.Length; i++)
            {
                CvInvoke.Rectangle(srcImg, rectangles[i], new MCvScalar(0, 0, 255), 2);//绘制矩形
            }
            CvInvoke.Imshow("result", srcImg);

            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 凸包，一般应用在物体识别，手势检测和边界检测
        /// </summary>
        public void ConvexHull()
        {
            //1，点集的凸包
            //Mat img = new Mat(500, 500, DepthType.Cv8U, 3);
            //img.SetTo(new MCvScalar(0));

            //VectorOfPoint vPoints = new VectorOfPoint();
            //Random random = new Random();
            //while (true)
            //{
            //    vPoints.Clear();
            //    img.SetTo(new MCvScalar(0));

            //    //产生随机点的数量3---60个之间
            //    int count = random.Next(3, 60);
            //    for (int i = 0; i < count; i++)
            //    {
            //        Point[] pt = new Point[1];
            //        pt[0].X = random.Next(img.Cols / 5, img.Cols * 4 / 5);
            //        pt[0].Y = random.Next(img.Rows / 5, img.Rows * 4 / 5);
            //        vPoints.Push(pt);
            //        CvInvoke.Circle(img, pt[0], 5, new MCvScalar(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255)), -1);
            //    }

            //    VectorOfPoint hull = new VectorOfPoint();

            //    //寻找凸包
            //    CvInvoke.ConvexHull(vPoints, hull);
            //    for (int j = 0; j < hull.Size; j++)
            //    {
            //        if (j != hull.Size - 1)
            //        { 
            //            CvInvoke.Line(img, hull[j], hull[j + 1], new MCvScalar(0, 255, 0), 1);
            //        }
            //        else
            //        {
            //            CvInvoke.Line(img, hull[j], hull[0], new MCvScalar(0, 255, 0), 1);
            //        }
            //    }
            //    CvInvoke.Imshow("Points-Hull", img);
            //    int key = CvInvoke.WaitKey(0);
            //    if (27 == key)
            //    {
            //        break;
            //    }
            //}

            //2，轮廓的凸包
            //Mat srcImg = CvInvoke.Imread("images/25.jpg");
            //CvInvoke.Imshow("src", srcImg);

            ////转成灰度图并二值化
            //Mat grayImg = new Mat();
            //CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            //CvInvoke.Threshold(grayImg, grayImg, 100, 255, ThresholdType.BinaryInv);
            //CvInvoke.Imshow("gray", grayImg);

            //VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            //CvInvoke.FindContours(grayImg, contours, null, RetrType.External, ChainApproxMethod.ChainApproxNone);
            //CvInvoke.DrawContours(srcImg, contours, -1, new MCvScalar(255, 0, 255), 2);
            //CvInvoke.Imshow("contour", srcImg);

            //for (int i = 0; i < contours.Size; i++)
            //{
            //    VectorOfPoint hull = new VectorOfPoint();
            //    CvInvoke.ConvexHull(contours[i], hull);
            //    for (int j = 0; j < hull.Size; j++)
            //    {
            //        if (j != hull.Size - 1)
            //        {
            //            CvInvoke.Line(srcImg, hull[j], hull[j + 1], new MCvScalar(0, 255, 0), 2);
            //        }
            //        else
            //        {
            //            CvInvoke.Line(srcImg, hull[j], hull[0], new MCvScalar(0, 255, 0), 2);
            //        }
            //    }
            //    CvInvoke.Imshow("Contous-Hull", srcImg);
            //    CvInvoke.WaitKey(0);
            //}

            //3，凸包缺陷分析
            Mat srcImg = CvInvoke.Imread("images/24.png");
            CvInvoke.Imshow("src", srcImg);
            Mat result = srcImg.Clone();

            Mat grayImg = new Mat();
            CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            CvInvoke.Threshold(grayImg, grayImg, 100, 255, ThresholdType.Binary);
            CvInvoke.Imshow("gray", grayImg);

            //寻找轮廓并绘制
            VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            CvInvoke.FindContours(grayImg, contours, null, RetrType.External, ChainApproxMethod.ChainApproxNone);
            CvInvoke.DrawContours(result, contours, -1, new MCvScalar(255), 2);

            for (int i = 0; i < contours.Size; i++)
            {
                //寻找凸包并绘制
                VectorOfInt hull = new VectorOfInt();
                CvInvoke.ConvexHull(contours[i], hull);
                for (int j = 0; j < hull.Size; j++)
                {
                    CvInvoke.Circle(result, contours[i][hull[j]], 5, new MCvScalar(255, 255, 0), -1);
                    if (j != hull.Size - 1)
                    {
                        CvInvoke.Line(result, contours[i][hull[j]], contours[i][hull[j + 1]], new MCvScalar(0, 255, 0), 2);
                    }
                    else
                    {
                        CvInvoke.Line(result, contours[i][hull[j]], contours[i][hull[0]], new MCvScalar(0, 255, 0), 2);
                    }
                }

                //凸包缺陷分析
                Mat defects = new Mat();
                CvInvoke.ConvexityDefects(contours[i], hull, defects);
                if (!defects.IsEmpty)
                {
                    //1行4列
                    using (Matrix<int> m = new Matrix<int>(defects.Rows, defects.Cols, defects.NumberOfChannels))
                    {

                        defects.CopyTo(m);
                        //you can iterate through the defect here:
                        for (int j = 0; j < m.Rows; j++)
                        {
                            int startIdx = m.Data[j, 0]; //起始点在轮廓上的索引
                            int endIdx = m.Data[j, 1];
                            int farthestPtIdx = m.Data[j, 2];
                            double Distance = m.Data[j, 3] / 256.0;
                            Point startPoint = contours[i][startIdx];
                            Point endPoint = contours[i][endIdx];
                            Point farPt = contours[i][farthestPtIdx];

                            //draw  a line connecting the convexity defect start point and end point in thin red line
                            Console.WriteLine("distance = {0}", Distance);
                            //CvInvoke.Line(result, startPoint, endPoint, new MCvScalar(0, 255, 0), 2);
                            CvInvoke.Circle(result, farPt, 5, new MCvScalar(0, 0, 255), -1);
                        }
                    }
                }
            }
            CvInvoke.Imshow("result", result);
            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 轮廓外接矩形
        /// </summary>
        public void ContoursBoundingRectangle()
        {
            //1，轮廓外接矩形
            //Mat srcImg = CvInvoke.Imread("images/23.jpg");
            //CvInvoke.Imshow("src", srcImg);
            //Mat cloneSrcImg = srcImg.Clone();

            ////转成灰度图并二值化
            //Mat grayImg = new Mat();
            //CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            //CvInvoke.Threshold(grayImg, grayImg, 100, 255, ThresholdType.Binary);
            //CvInvoke.Imshow("gray", grayImg);

            ////查找轮廓并绘制
            //VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            //CvInvoke.FindContours(grayImg, contours, null, RetrType.External, ChainApproxMethod.ChainApproxNone);
            //CvInvoke.DrawContours(cloneSrcImg, contours, -1, new MCvScalar(0, 0, 255), 2);

            //for (int i = 0; i < contours.Size; i++)
            //{
            //    //使轮廓外接矩形
            //    Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
            //    CvInvoke.Rectangle(cloneSrcImg, rect, new MCvScalar(0, 255, 0), 2);
            //}
            //CvInvoke.Imshow("src-clone", cloneSrcImg);

            //2，实战分割估值表
            //Mat srcImg = CvInvoke.Imread("images/9.png");
            //CvInvoke.Imshow("src", srcImg);
            //Mat cloneSrcImg = srcImg.Clone();

            ////转成灰度图并二值化
            //Mat grayImg = new Mat();
            //CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            //CvInvoke.Threshold(grayImg, grayImg, 220, 255, ThresholdType.BinaryInv);
            //CvInvoke.Imshow("gray", grayImg);

            ////查找轮廓并绘制
            //VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            //CvInvoke.FindContours(grayImg, contours, null, RetrType.External, ChainApproxMethod.ChainApproxNone);
            //CvInvoke.DrawContours(cloneSrcImg, contours, -1, new MCvScalar(255, 0, 0), 2);
            //CvInvoke.Imshow("contours", cloneSrcImg);

            //for (int i = 0; i < contours.Size; i++)
            //{
            //    //使轮廓外接矩形
            //    Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
            //    if (rect.Width > 450 && rect.Height > 200)//利用矩形的Width和Height属性过滤干扰项
            //    {
            //        CvInvoke.Rectangle(cloneSrcImg, rect, new MCvScalar(0, 0, 255), 2);
            //    }
            //}
            //CvInvoke.Imshow("src-clone", cloneSrcImg);

            //3，实战分割硬币
            Mat srcImg = CvInvoke.Imread("images/26.png");
            CvInvoke.Imshow("src", srcImg);
            Mat cloneSrcImg = srcImg.Clone();

            //转成灰度图并二值化
            Mat grayImg = new Mat();
            CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            CvInvoke.Threshold(grayImg, grayImg, 220, 255, ThresholdType.Binary);
            CvInvoke.Imshow("gray", grayImg);

            //膨胀
            Mat element = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), new Point(-1, -1));
            CvInvoke.Dilate(grayImg, grayImg, element, new Point(-1, -1), 1, BorderType.Default, new MCvScalar());
            CvInvoke.Imshow("dilate", grayImg);

            //查找轮廓并绘制
            VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            CvInvoke.FindContours(grayImg, contours, null, RetrType.External, ChainApproxMethod.ChainApproxNone);
            CvInvoke.DrawContours(cloneSrcImg, contours, -1, new MCvScalar(255, 0, 0), 2);
            CvInvoke.Imshow("contours", cloneSrcImg);

            for (int i = 0; i < contours.Size; i++)
            {
                //使轮廓外接矩形
                Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
                if (rect.Width > 25 && rect.Height > 25)//利用矩形的Width和Height属性过滤干扰项
                {
                    CvInvoke.Rectangle(cloneSrcImg, rect, new MCvScalar(0, 0, 255), 2);
                }
            }
            CvInvoke.Imshow("src-clone", cloneSrcImg);

            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 轮廓最小外接矩形
        /// </summary>
        public void ContoursMinAreaRect()
        {
            //1，最小外接矩形
            //Mat srcImg = CvInvoke.Imread("images/33.jpg");
            //CvInvoke.Imshow("src", srcImg);
            //Mat result = srcImg.Clone();

            ////转成灰度图并二值化
            //Mat grayImg = new Mat();
            //CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            //CvInvoke.Threshold(grayImg, grayImg, 100, 255, ThresholdType.Binary);
            //CvInvoke.Imshow("gray", grayImg);

            ////查找并绘制轮廓
            //VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            //CvInvoke.FindContours(grayImg, contours, null, RetrType.External, ChainApproxMethod.ChainApproxNone);
            //CvInvoke.DrawContours(result, contours, -1, new MCvScalar(255, 0, 255), 1);

            //for (int i = 0; i < contours.Size; i++)
            //{
            //    //绘制正外接矩形
            //    Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
            //    CvInvoke.Rectangle(result, rect, new MCvScalar(0, 255, 0), 2);

            //    //绘制最小外接矩形
            //    RotatedRect rotatedRect = CvInvoke.MinAreaRect(contours[i]);
            //    PointF[] pts = rotatedRect.GetVertices();//返回外界矩形的顶点
            //    for (int j = 0; j < pts.Length; j++)
            //    {
            //        CvInvoke.Line(result, new Point((int)pts[j].X, (int)pts[j].Y), new Point((int)pts[(j + 1) % 4].X, (int)pts[(j + 1) % 4].Y), new MCvScalar(0, 0, 255), 2);
            //    }
            //}
            //CvInvoke.Imshow("result", result);

            //2，实战计算物体宽高
            //Mat srcImg = CvInvoke.Imread("images/32.jpg");
            //CvInvoke.Imshow("src", srcImg);
            //Mat result = srcImg.Clone();

            ////转成灰度图并二值化
            //Mat grayImg = new Mat();
            //CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            //CvInvoke.Threshold(grayImg, grayImg, 100, 255, ThresholdType.BinaryInv);
            //CvInvoke.Imshow("gray", grayImg);

            ////查找并绘制轮廓
            //VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            //CvInvoke.FindContours(grayImg, contours, null, RetrType.External, ChainApproxMethod.ChainApproxNone);
            //CvInvoke.DrawContours(result, contours, -1, new MCvScalar(255, 0, 255), 1);

            //for (int i = 0; i < contours.Size; i++)
            //{
            //    //绘制正外接矩形
            //    Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
            //    CvInvoke.Rectangle(result, rect, new MCvScalar(0, 255, 0), 2);

            //    //绘制最小外接矩形
            //    RotatedRect rotatedRect = CvInvoke.MinAreaRect(contours[i]);
            //    PointF[] pts = rotatedRect.GetVertices();//返回外界矩形的顶点
            //    for (int j = 0; j < pts.Length; j++)
            //    {
            //        CvInvoke.Line(result, new Point((int)pts[j].X, (int)pts[j].Y), new Point((int)pts[(j + 1) % 4].X, (int)pts[(j + 1) % 4].Y), new MCvScalar(0, 0, 255), 2);
            //    }

            //    //绘制圆心
            //    CvInvoke.Circle(result, new Point((int)rotatedRect.Center.X, (int)rotatedRect.Center.Y), 3, new MCvScalar(0, 255, 0), -1);

            //    //添加文字
            //    CvInvoke.PutText(result, string.Format("width:{0:N2}", rotatedRect.Size.Width), new Point(225, 240), FontFace.HersheyComplexSmall, 0.8, new MCvScalar(255, 0, 0));
            //    CvInvoke.PutText(result, string.Format("height:{0:N2}", rotatedRect.Size.Height), new Point(225, 265), FontFace.HersheyComplexSmall, 0.8, new MCvScalar(255, 0, 0));
            //}
            //CvInvoke.Imshow("result", result);

            //3，实战旋转物体矫正
            Mat srcImg = CvInvoke.Imread("images/34.jpg");
            CvInvoke.Imshow("src", srcImg);
            Mat result = srcImg.Clone();

            //转成灰度图并二值化
            Mat grayImg = new Mat();
            CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            CvInvoke.Threshold(grayImg, grayImg, 100, 255, ThresholdType.BinaryInv);
            CvInvoke.Imshow("gray", grayImg);

            //膨胀和腐蚀
            Mat element = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(11, 11), new Point(-1, -1));
            CvInvoke.Dilate(grayImg, grayImg, element, new Point(-1, -1), 1, BorderType.Default, new MCvScalar());
            CvInvoke.Imshow("dilate", grayImg);

            CvInvoke.Erode(grayImg, grayImg, element, new Point(-1, -1), 1, BorderType.Default, new MCvScalar());
            CvInvoke.Imshow("erode", grayImg);

            //查找并绘制轮廓
            VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            CvInvoke.FindContours(grayImg, contours, null, RetrType.External, ChainApproxMethod.ChainApproxNone);
            CvInvoke.DrawContours(result, contours, -1, new MCvScalar(255, 0, 255), 1);

            double angle = 0;
            PointF rotateCenter = new PointF();

            for (int i = 0; i < contours.Size; i++)
            {
                //绘制正外接矩形
                Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);

                //过滤干扰项
                if (rect.Width < 200 || rect.Height < 200
                    || rect.Width > 550 || rect.Height > 450)
                {
                    continue;
                }

                CvInvoke.Rectangle(result, rect, new MCvScalar(0, 255, 0), 2);

                //绘制最小外接矩形
                RotatedRect rotatedRect = CvInvoke.MinAreaRect(contours[i]);
                PointF[] pts = rotatedRect.GetVertices();//返回外界矩形的顶点
                for (int j = 0; j < pts.Length; j++)
                {
                    CvInvoke.Line(result, new Point((int)pts[j].X, (int)pts[j].Y), new Point((int)pts[(j + 1) % 4].X, (int)pts[(j + 1) % 4].Y), new MCvScalar(0, 0, 255), 2);
                }

                angle = rotatedRect.Angle;
                rotateCenter = rotatedRect.Center;
            }

            //旋转矫正
            CvInvoke.Imshow("rotate---before", result);
            if (45 < Math.Abs(angle) && Math.Abs(angle) < 90)
            {
                angle = 90 - Math.Abs(angle);
            }
            Mat rotateM = new Mat();
            CvInvoke.GetRotationMatrix2D(rotateCenter, angle, 1, rotateM);
            CvInvoke.WarpAffine(result, result, rotateM, result.Size);
            CvInvoke.Imshow("result", result);

            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 轮廓最小外接圆
        /// </summary>
        public void ContoursEnclosingCircle()
        {
            //1，最小外接圆
            //Mat srcImg = CvInvoke.Imread("images/37.png");
            //CvInvoke.Imshow("src", srcImg);
            //Mat result = srcImg.Clone();

            ////创建一个全黑的图像，用于展示逼近多边形曲线结果
            //Mat blackImg = new Mat(srcImg.Size, DepthType.Cv8U, 3);
            //blackImg.SetTo(new MCvScalar(0));

            ////转成灰度图并二值化
            //Mat grayImg = new Mat();
            //CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            //CvInvoke.Threshold(grayImg, grayImg, 100, 255, ThresholdType.Binary);
            //CvInvoke.Imshow("gray", grayImg);

            ////查找并绘制轮廓
            //VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            //CvInvoke.FindContours(grayImg, contours, null, RetrType.External, ChainApproxMethod.ChainApproxNone);
            //CvInvoke.DrawContours(result, contours, -1, new MCvScalar(255, 0, 255), 1);

            //for (int i = 0; i < contours.Size; i++)
            //{
            //    //最小外接圆
            //    //CircleF circle = CvInvoke.MinEnclosingCircle(contours[i]);
            //    //CvInvoke.Circle(result, new Point((int)circle.Center.X, (int)circle.Center.Y), (int)circle.Radius, new MCvScalar(0, 255, 0), 2);

            //    //椭圆拟合
            //    //try
            //    //{
            //    //    RotatedRect ellipse = CvInvoke.FitEllipse(contours[i]);
            //    //    //CvInvoke.Ellipse(result, ellipse, new MCvScalar(0, 255, 0), 2); //绘制有错误
            //    //    CvInvoke.Ellipse(result, new Point((int)ellipse.Center.X, (int)ellipse.Center.Y),new Size((int)ellipse.Size.Width / 2, (int)ellipse.Size.Height / 2), ellipse.Angle, 0, 360, new MCvScalar(0, 255, 0), 2);
            //    //}
            //    //catch(Exception ex)
            //    //{
            //    //    continue;
            //    //}

            //    //多边形逼近、轮廓面积、周长
            //    double length = CvInvoke.ArcLength(contours[i], true);
            //    double area = CvInvoke.ContourArea(contours[i]);
            //    Console.WriteLine("contour_length = {0:N2}", length);
            //    Console.WriteLine("contour_area = {0:N2}", area);

            //    VectorOfPoint approxPoly = new VectorOfPoint();
            //    CvInvoke.ApproxPolyDP(contours[i], approxPoly, length * 0.02, true);
            //    for (int j = 0; j < approxPoly.Size; j++)
            //    {
            //        CvInvoke.Line(blackImg, approxPoly[j], approxPoly[(j + 1) % approxPoly.Size], new MCvScalar(0, 255, 255), 2);
            //        CvInvoke.Line(result, approxPoly[j], approxPoly[(j + 1) % approxPoly.Size], new MCvScalar(0, 0, 255), 2);
            //    }
            //}
            //CvInvoke.Imshow("black", blackImg);
            //CvInvoke.Imshow("result", result);

            //2，利用mask提取不规则轮廓
            Mat srcImg = CvInvoke.Imread("images/35.jpg");
            CvInvoke.Imshow("src", srcImg);

            Mat tempImg = srcImg.Clone();
            Mat blackImg = new Mat(srcImg.Size, DepthType.Cv8U, 3);//创建一个背景为全黑的图像
            blackImg.SetTo(new MCvScalar(0));
            Mat draw = blackImg.Clone();
            Mat result = blackImg.Clone();

            Mat grayImg = new Mat();
            CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            CvInvoke.Imshow("gray", grayImg);

            CvInvoke.GaussianBlur(grayImg, grayImg, new Size(3, 3), 0);
            CvInvoke.Imshow("gaussian", grayImg);

            CvInvoke.Threshold(grayImg, grayImg, 100, 255, ThresholdType.BinaryInv);
            CvInvoke.Imshow("threshold", grayImg);

            //查找并绘制轮廓
            VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            CvInvoke.FindContours(grayImg, contours, null, RetrType.Tree, ChainApproxMethod.ChainApproxNone);
            while (true)
            {
                for (int i = 0; i < contours.Size; i++)
                {
                    blackImg.CopyTo(draw);
                    blackImg.CopyTo(result);
                    CvInvoke.DrawContours(draw, contours, i, new MCvScalar(255, 255, 255), -1);//绘制第 i 个轮廓

                    //创建掩码
                    Mat mask = new Mat();
                    CvInvoke.CvtColor(draw, mask, ColorConversion.Bgr2Gray);
                    tempImg.CopyTo(result, mask);
                    CvInvoke.Imshow("mask", mask);
                    CvInvoke.Imshow("result", result);
                    int key = CvInvoke.WaitKey(0);
                    if (27 == key)
                    {
                        break;
                    }
                }
                break;
            }

            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 轮廓位置关系与轮廓匹配
        /// </summary>
        public void ContoursPointPolygon()
        {
            //1，计算点与轮廓的距离及位置关系
            //Mat srcImg = CvInvoke.Imread("images/22.png");
            //CvInvoke.Imshow("src", srcImg);
            //Mat result = srcImg.Clone();

            //Mat grayImg = new Mat();
            //CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
            //CvInvoke.Imshow("gray", grayImg);

            //CvInvoke.GaussianBlur(grayImg, grayImg, new Size(3, 3), 0);
            //CvInvoke.Imshow("gaussian", grayImg);

            //CvInvoke.Threshold(grayImg, grayImg, 100, 255, ThresholdType.Binary);
            //CvInvoke.Imshow("threshold", grayImg);

            //VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            //CvInvoke.FindContours(grayImg, contours, null, RetrType.External, ChainApproxMethod.ChainApproxNone);

            //for (int i = 0; i < contours.Size; i++)
            //{
            //    CvInvoke.Circle(result, new Point(100, 100), 3, new MCvScalar(0, 255, 0), -1);
            //    double ret = CvInvoke.PointPolygonTest(contours[i], new PointF(100, 100), false);
            //    Console.WriteLine("ret = {0}", ret);
            //}
            //CvInvoke.Imshow("result", result);

            //2，形状匹配
            Mat tempImg = CvInvoke.Imread("images/39.jpg");
            CvInvoke.Imshow("temp", tempImg);

            Mat gray = new Mat();
            CvInvoke.CvtColor(tempImg, gray, ColorConversion.Bgr2Gray);
            CvInvoke.Threshold(gray, gray, 100, 255, ThresholdType.Binary);
            VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            CvInvoke.FindContours(gray, contours, null, RetrType.External, ChainApproxMethod.ChainApproxNone);

            Mat srcImg = CvInvoke.Imread("images/40.jpg");
            Mat gray2 = new Mat();
            CvInvoke.CvtColor(srcImg, gray2, ColorConversion.Bgr2Gray);
            CvInvoke.Threshold(gray2, gray2, 100, 255, ThresholdType.Binary);
            VectorOfVectorOfPoint contours2 = new VectorOfVectorOfPoint();
            CvInvoke.FindContours(gray2, contours2, null, RetrType.External, ChainApproxMethod.ChainApproxNone);

            for (int i = 0; i < contours2.Size; i++)
            {
                double matchValue = CvInvoke.MatchShapes(contours[0], contours2[i], ContoursMatchType.I1);
                Console.WriteLine("matchValue = {0}", matchValue);
                if (matchValue <= 0.1)
                {
                    CvInvoke.DrawContours(srcImg, contours2, i, new MCvScalar(0, 255, 0), -1);
                }
                else
                {
                    CvInvoke.DrawContours(srcImg, contours2, i, new MCvScalar(0, 0, 255), -1);
                }
                CvInvoke.Imshow("result", srcImg);
                CvInvoke.WaitKey(0);
            }

            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 滑块拼图验证码
        /// </summary>
        public void SliderVerificationCode()
        {
            //加载原图
            Mat srcImg = CvInvoke.Imread("images/43.png");
            CvInvoke.Imshow("src", srcImg);
            Mat srcImg2 = srcImg.Clone();

            //转化为HSV并分离通道
            Mat hsvImg = new Mat();
            CvInvoke.CvtColor(srcImg2, hsvImg, ColorConversion.Bgr2Hsv);
            CvInvoke.Imshow("hsv", hsvImg);
            var channels = hsvImg.Split();

            //取V通道并做二值化
            Mat thresholdImg = new Mat();
            CvInvoke.Threshold(channels[2], thresholdImg, 200, 255, ThresholdType.BinaryInv);
            CvInvoke.Imshow("threshold", thresholdImg);

            //形态学处理，开关运算
            Mat morphologyExImg = new Mat();
            Mat element = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(5, 5), new Point(-1, -1));
            CvInvoke.MorphologyEx(thresholdImg, morphologyExImg, MorphOp.Open, element, new Point(-1, -1), 1, BorderType.Default, new MCvScalar());//开运算是先腐蚀后膨胀的过程, 开运算可以用来消除小物体
            CvInvoke.Imshow("morphologyEx-open", morphologyExImg);

            CvInvoke.MorphologyEx(morphologyExImg, morphologyExImg, MorphOp.Close, element, new Point(-1, -1), 1, BorderType.Default, new MCvScalar());//闭运算是先膨胀后腐蚀的过程, 闭运算可以用来消除小型黑洞
            CvInvoke.Imshow("morphologyEx-close", morphologyExImg);

            //查找轮廓并绘制轮廓
            VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            CvInvoke.FindContours(morphologyExImg, contours, null, RetrType.External, ChainApproxMethod.ChainApproxNone);
            for (int i = 0; i < contours.Size; i++)
            {
                Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
                if (rect.Width > 50 && rect.Width < 70 && rect.Height > 50 && rect.Height < 70)//利用矩形的Width和Height属性过滤干扰项
                {
                    CvInvoke.Rectangle(srcImg, rect, new MCvScalar(0, 0, 255), 2);
                }
            }
            CvInvoke.Imshow("result", srcImg);

            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 轮廓颜色识别
        /// </summary>
        public void ContoursColorRecognition()
        {
            Mat srcImg = CvInvoke.Imread("images/44.png");
            CvInvoke.Imshow("src", srcImg);

            Mat hsvImg = new Mat();
            Mat mask = new Mat();

            double h_min = 156, s_min = 43, v_min = 46;
            double h_max = 180, s_max = 255, v_max = 255;
            ScalarArray hsv_min = new ScalarArray(new MCvScalar(h_min, s_min, v_min));
            ScalarArray hsv_max = new ScalarArray(new MCvScalar(h_max, s_max, v_max));
            CvInvoke.CvtColor(srcImg, hsvImg, ColorConversion.Bgr2Hsv);//颜色转换
            //CvInvoke.Imshow("hsvImg", hsvImg);

            CvInvoke.InRange(hsvImg, hsv_min, hsv_max, mask);
            CvInvoke.MedianBlur(mask, mask, 5);//中值滤波去除小的杂讯
            CvInvoke.Imshow("mask", mask);

            //查找轮廓
            VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            CvInvoke.FindContours(mask, contours, null, RetrType.External, ChainApproxMethod.ChainApproxNone);
            for (int i = 0; i < contours.Size; i++)
            {
                Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);//使用正外接矩形标识轮廓
                CvInvoke.Rectangle(srcImg, rect, new MCvScalar(0, 255, 255), 2);
                CvInvoke.PutText(srcImg, "red", new Point(rect.X, rect.Y), FontFace.HersheyComplexSmall, 1.2, new MCvScalar(0, 255, 0), 2);
            }
            CvInvoke.Imshow("result", srcImg);
            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 图像修补
        /// </summary>
        public void ImageInPaint()
        {
            //示例1
            //Mat srcImg = CvInvoke.Imread("images/47.jpg");
            //CvInvoke.Imshow("src", srcImg);
            //Mat dstImg = srcImg.Clone();

            //Mat mask = new Mat(srcImg.Size, DepthType.Cv8U, 1);
            //mask.SetTo(new MCvScalar(0));

            //CvInvoke.Rectangle(mask, new Rectangle(33, 274, 124, 109), new MCvScalar(255), -1);
            //CvInvoke.Rectangle(mask, new Rectangle(187, 322, 256, 51), new MCvScalar(255), -1);
            //CvInvoke.Imshow("mask", mask);

            //CvInvoke.Inpaint(srcImg, mask, dstImg, 1, InpaintType.NS);
            //CvInvoke.Imshow("result", dstImg);

            //示例2
            Mat srcImg = CvInvoke.Imread("images/49.jpg");
            CvInvoke.Imshow("src", srcImg);
            Mat dstImg = srcImg.Clone();

            Mat mask = CvInvoke.Imread("images/50.jpg", ImreadModes.Grayscale);
            CvInvoke.Imshow("mask", mask);

            CvInvoke.Inpaint(srcImg, mask, dstImg, 1, InpaintType.NS);
            CvInvoke.Imshow("result", dstImg);
            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// GrabCut和FloodFill图像分割算法
        /// </summary>
        public void GrabCutAndFloodFill()
        {
            //使用Rect初始化GrabCut进行分割
            //Mat srcImg = CvInvoke.Imread("images/54.jpg");
            //CvInvoke.Imshow("src", srcImg);
            //Mat tempImg = srcImg.Clone();

            //Rectangle rect = new Rectangle(85, 84, 406, 318); //bird.jpg
            //Mat bgModel = new Mat();
            //Mat fgModel = new Mat();
            //Mat mask = new Mat();

            //CvInvoke.GrabCut(srcImg, mask, rect, bgModel, fgModel, 1, GrabcutInitType.InitWithRect);
            //CvInvoke.Imshow("grab", mask);

            //CvInvoke.Rectangle(srcImg, rect, new MCvScalar(255, 0, 0), 2);
            //CvInvoke.Imshow("rect", srcImg);

            //Mat prBGD = new Mat(mask.Size, DepthType.Cv8U, 1);
            //prBGD.SetTo(new MCvScalar(3));

            //CvInvoke.Compare(mask, prBGD, mask, CmpType.Equal);
            //CvInvoke.Imshow("compare", mask);

            //Mat dstImg = new Mat();
            //tempImg.CopyTo(dstImg, mask);
            //CvInvoke.Imshow("result", dstImg);

            //FloodFill算法
            //Mat srcImg = CvInvoke.Imread("images/51.jpg");
            //CvInvoke.Imshow("src", srcImg);
            //Mat tempImg = srcImg.Clone();

            //Rectangle rect = new Rectangle();
            //Mat mask = new Mat();

            //Point seedPoint = new Point(80, 80);
            //CvInvoke.FloodFill(srcImg, mask, seedPoint, new MCvScalar(255, 0, 255), out rect,new MCvScalar(5, 5, 5), new MCvScalar(5, 5, 5));
            //CvInvoke.Circle(srcImg, seedPoint, 3, new MCvScalar(0, 255, 0), -1);
            //CvInvoke.Imshow("result", srcImg);

            Mat img = CvInvoke.Imread("");
            var v = img.ToBitmap();

            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// PaddleOCR入门
        /// </summary>
        public void BeginPaddleOCR()
        {
            throw new NotImplementedException();
        }

        #region Private method
        /// <summary>
        /// 图像平移实现函数
        /// </summary>
        /// <param name="srcImg">源图像</param>
        /// <param name="dstImg">目标图像</param>
        /// <param name="xOffset"></param>
        /// <param name="yOffset"></param>
        private void ImgTranslate(Image<Bgr, Byte> srcImg, Image<Bgr, Byte> dstImg, int xOffset, int yOffset)
        {
            for (int i = 0; i < srcImg.Rows; i++)
            {
                for (int j = 0; j < srcImg.Cols; j++)
                {
                    int x = j + xOffset;
                    int y = i + yOffset;
                    if (x >= 0 && x < dstImg.Cols && y >= 0 && y < dstImg.Rows)
                    {
                        dstImg[y, x] = srcImg[i, j];
                    }
                }
            }
        }
        #endregion

    }
}
